import java.io.*;
import java.lang.reflect.Field;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * 模拟Modbus RTU Over TCP的情况，模拟Slave端（电池探测器端）
 * 大端模式
 */

public class MBJavaSingleSlave extends Thread {

    public static void main(String[] args) {
        try {
            Thread t = new MBJavaSingleSlave(502);
            t.run();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private ServerSocket serverSocket;
    public static List<byte[]> batDataList;

    public MBJavaSingleSlave(int port) throws IOException {
        serverSocket = new ServerSocket(port);
        serverSocket.setSoTimeout(0);
    }

    public void run() {
        // 首先把16个电池的假数据处理好
        int totalLen = 0;
        for (PointInfo p :
                PointTable.pointTable) {
            totalLen += p.getLen();
        }
        batDataList = new ArrayList<>();
        for (int i = 1; i <= 16; i++) {
            ByteBuffer singleBatData = ByteBuffer.allocate(totalLen);
//            returnBr.put(hexStringToByteArray(returnHead));
            for (PointInfo p :
                    PointTable.pointTable) {
                switch (p.type) {
                    case "u8":
                    case "u16":
                        switch (p.entityFieldName) {
                            case "current_work_station":
                                List<Short> list = Arrays.asList((short) 0, (short) 1, (short) 2, (short) 3, (short) 4, (short) 6, (short) 7);
                                Collections.shuffle(list);
                                singleBatData.putShort(list.get(0));
                                break;
                            case "WORK_mode":
                                list = Arrays.asList((short) 1, (short) 2, (short) 3, (short) 7);
                                Collections.shuffle(list);
                                singleBatData.putShort(list.get(0));
                                break;
                            default:
                                singleBatData.putShort((short) i);
                                break;
                        }
                        // 结合len值进行判断，这是因为点表里面有类型为u8但是占了两个寄存器的数据，如Comm0Add
                        if (p.len == 4) {
                            singleBatData.putShort((short) 0);
                        }
                        break;
                    case "float":
                        singleBatData.putFloat(3.14f);
                        break;
                }
            }
            batDataList.add(singleBatData.array());
//            System.out.println(bytesToHexString(singleBatData.array()));
        }
//        MBJavaSingleSlaveGUI.onRefresh();

        while (true) {
            try {
                System.out.println("等待远程连接，端口号为：" + serverSocket.getLocalPort() + "...");
                Socket server = serverSocket.accept();
                System.out.println("远程主机地址：" + server.getRemoteSocketAddress());
                DataInputStream in = new DataInputStream(server.getInputStream());
                byte[] resultBuff = new byte[0];
                byte[] buff = new byte[1024];
                int k = -1;
                while (true) {
//                    System.out.println();
                    k = in.read(buff, 0, buff.length);
                    if (k == -1) {
                        break;
                    }
                    byte[] tbuff = new byte[resultBuff.length + k]; // temp buffer size = bytes already read + bytes last read
                    System.arraycopy(resultBuff, 0, tbuff, 0, resultBuff.length); // copy previous bytes
                    System.arraycopy(buff, 0, tbuff, resultBuff.length, k);  // copy current lot
                    resultBuff = tbuff; // call the temp buffer as your result buff
                    if (in.available() == 0) {
                        break;
                    }
                }
                System.out.println(resultBuff.length + " bytes read.");
                System.out.println(bytesToHexString(resultBuff));

                ByteBuffer bf = ByteBuffer.wrap(resultBuff);

                int address = Integer.valueOf(bf.get());
                int func = Integer.valueOf(bf.get());
                System.out.println("address: " + address);
                System.out.println("func: " + func);
                switch (func) {
                    case 3:
                        // 读取
                        int regStartAddress = bf.getShort();
                        int quantity = bf.getShort();
                        System.out.println("regSA: " + regStartAddress);
                        System.out.println("quantity: " + quantity);
                        String returnHead = u8To1BytesHexString(address) + u8To1BytesHexString(func) + u8To1BytesHexString(quantity * 2);
                        ByteBuffer returnBr = ByteBuffer.allocate(quantity * 2 + 3);
                        returnBr.put(hexStringToByteArray(returnHead));
                        byte[] thisBatData = batDataList.get(address - 1);
                        byte[] returnDataBytes = new byte[quantity * 2];
                        for (int i = 0; i < quantity * 2; i++) {
                            returnDataBytes[i] = thisBatData[i];
                        }
                        returnBr.put(returnDataBytes);
                        DataOutputStream out = new DataOutputStream(server.getOutputStream());
                        out.write(hexStringToByteArrayWithCRC16(bytesToHexString(returnBr.array())));
//                        out.write(returnBr.array());
                        out.flush();
                        break;
                    case 6:
                        // 写入单个
                        int tgtAddress = bf.getShort();
//                        int value = bf.getShort();
                        System.out.println("tgtAddress: " + tgtAddress);
                        System.out.println("value[RAW]: " + u8To1BytesHexString(bf.get()) + u8To1BytesHexString(bf.get()));
                        int tgtPos1 = tgtAddress * 2;
                        int tgtPos2 = tgtPos1 + 1;

                        // 如果是给广播地址00发的写入，那么所有的电池数据都要更改
                        if (address == 0) {
                            for (int i = 0; i < 16; i++) {
                                byte[] thisBat = batDataList.get(i);
                                bf.position(4);
                                byte b1 = bf.get();
                                byte b2 = bf.get();
                                System.out.println(b1);
                                System.out.println(b2);
                                thisBat[tgtPos1] = b1;
                                thisBat[tgtPos2] = b2;
                                System.out.println(bytesToHexString(thisBat));
                                batDataList.set(i, thisBat);
                            }
                        } else {
                            byte[] thisBat = batDataList.get(address - 1);
                            bf.position(4);
                            byte b1 = bf.get();
                            byte b2 = bf.get();
                            System.out.println(b1);
                            System.out.println(b2);
                            thisBat[tgtPos1] = b1;
                            thisBat[tgtPos2] = b2;
                            System.out.println(bytesToHexString(thisBat));
                            batDataList.set(address - 1, thisBat);
                        }
                        out = new DataOutputStream(server.getOutputStream());
                        out.write(bf.array());
                        out.flush();
                        break;
                    case 16:
                        // 写入多个
                        int tgtStartAddress = bf.getShort();
                        int regNum = bf.getShort();
                        byte bytesNum = bf.get();
                        System.out.println("tgtStartAddress: " + tgtStartAddress);
                        System.out.println("regNum: " + regNum);
                        System.out.println("bytesNum: " + Byte.toUnsignedInt(bytesNum));
                        byte[] tgtVal = new byte[bytesNum];
                        for (int i = 0; i < bytesNum; i++) {
                            tgtVal[i] = bf.get();
                        }

                        // 如果是给广播地址00发的写入，那么所有的电池数据都要更改
                        if (address == 0) {
                            for (int i = 0; i < 16; i++) {
                                byte[] thisBat = batDataList.get(i);
                                bf.position(7);
                                for (int j = 0; j < bytesNum; j++) {
                                    thisBat[tgtStartAddress * 2 + j] = tgtVal[j];
                                }
                                System.out.println(bytesToHexString(thisBat));
                                batDataList.set(i, thisBat);
                            }
                        } else {

//                        int tgtPos1 = tgtAddress * 2;
//                        int tgtPos2 = tgtPos1 + 1;
                            byte[] thisBat = batDataList.get(address - 1);
                            bf.position(7);
//                        byte b1 = bf.get();
//                        byte b2 = bf.get();
//                        System.out.println(b1);
//                        System.out.println(b2);
                            for (int i = 0; i < bytesNum; i++) {
                                thisBat[tgtStartAddress * 2 + i] = tgtVal[i];
                            }
//                        thisBat[tgtPos1] = b1;
//                        thisBat[tgtPos2] = b2;
                            System.out.println(bytesToHexString(thisBat));
                            batDataList.set(address - 1, thisBat);
                        }
                        out = new DataOutputStream(server.getOutputStream());
                        out.write(bf.array());
                        out.flush();
                        break;
                    default:
                        break;
                }


//                try {
//                    len = in.available();
//                    System.out.println("len: "+len);
//                    for (int i = 0; i < len; i++){
//                        int t = in.read();
//                        System.out.println(u8To2BytesHexString(t));
//                        System.out.println("remain: " + in.available());
//
//                    }
//                } catch (IOException e) {
//                    e.printStackTrace();
//                }
                System.out.println("end");

//                out.writeUTF("谢谢连接我：" + server.getLocalSocketAddress() + "\nGoodbye!");
                server.close();

            } catch (SocketTimeoutException s) {
                System.out.println("Socket timed out!");
                break;
            } catch (IOException e) {
                e.printStackTrace();
                break;
            }
        }

    }

    /**
     * float转换为字节
     *
     * @param value 要转换的float数
     * @return 一个byte数组
     */
    public static byte[] floatToByte(float value) {

        int intBits = Float.floatToIntBits(value);
        return new byte[]{
                (byte) (intBits >> 24), (byte) (intBits >> 16), (byte) (intBits >> 8), (byte) (intBits)};

    }

    /**
     * 使用03功能码读取
     *
     * @param host     IP地址
     * @param port     端口号
     * @param slaveID  从机号
     * @param address  读取的起始寄存器地址
     * @param quantity 读取的寄存器数量
     * @return 返回Data类型的对象
     */
    public static Data read(String host, int port, String slaveID, String address, String quantity) {
        Socket socket = null;
        try {
            System.out.println("Connecting...");
            socket = new Socket(host, port);
            System.out.println("Connection success");
            byte[] bytes = hexStringToByteArrayWithCRC16(slaveID + "03" + address + quantity); // 按照Modbus RTU拼接报文
            System.out.println("Send: " + bytesToHexString(bytes));
            OutputStream os = socket.getOutputStream();
            BufferedOutputStream bos = new BufferedOutputStream(os);
            bos.write(bytes);
            bos.flush();

            //读取服务器返回的消息
            byte[] receivedBytes = new byte[3];
            socket.getInputStream().read(receivedBytes);
            System.out.println("Receive Head: " + bytesToHexString(receivedBytes));
            if (receivedBytes[0] == bytes[0] && receivedBytes[1] == bytes[1]) {
                int len = Byte.toUnsignedInt(receivedBytes[2]);
                byte[] receivedData = new byte[len + 2];
                socket.getInputStream().read(receivedData);
                System.out.println("Receive Data: " + bytesToHexString(receivedData));

                // 按点表处理数据
                int offset = 0;
                Data data = new Data();
                for (PointInfo p : PointTable.pointTable) {
                    setValueToEntityByPointData(receivedData, offset, data, p);
                    offset = offset + p.getLen();
                }
                return data;
                // 此处得到的data对象就是读出来的数据实体了
            } else {
                System.out.println("ERR");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (socket != null) {
                try {
                    socket.close();
                } catch (Exception ignored) {

                }
            }
        }
        return null;
    }

    /**
     * 使用06功能码写入单个寄存器
     *
     * @param host         IP地址
     * @param port         端口号
     * @param slaveID      从机号
     * @param writeAddress 要写入的寄存器地址
     * @param writeValue   要写入的值
     */
    public static void writeSingleRegister(String host, int port, String slaveID, String writeAddress, String writeValue) {
        Socket socket = null;
        try {
            System.out.println("Connecting...");
            socket = new Socket(host, port);
            System.out.println("Connection success");

            byte[] bytes = hexStringToByteArrayWithCRC16(slaveID + "06" + writeAddress + writeValue); // 按照Modbus RTU拼接报文
            System.out.println("Send: " + bytesToHexString(bytes));
            OutputStream os = socket.getOutputStream();
            BufferedOutputStream bos = new BufferedOutputStream(os);
            bos.write(bytes);
            bos.flush();

            //读取服务器返回的消息
            byte[] receivedBytes = new byte[bytes.length];
            socket.getInputStream().read(receivedBytes);
            System.out.println("Receive: " + bytesToHexString(receivedBytes));
            if (bytesToHexString(receivedBytes).equals(bytesToHexString(bytes))) {
                System.out.println("Write successfully");
            } else {
                System.out.println("ERR");
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (socket != null) {
                try {
                    socket.close();
                } catch (Exception ignored) {

                }
            }
        }
    }

    /**
     * 使用16功能码写入Float型数据
     *
     * @param host         IP地址
     * @param port         端口
     * @param slaveID      从机地址
     * @param writeAddress 寄存器地址
     * @param f            要写入的Float数，如果直接输入小数会被认为是Double，建议Float.valveOf("xxx")
     */
    public static void writeFloat(String host, int port, String slaveID, String writeAddress, Float f) {
        byte[] b = floatToByte(f);
        String writeValue = bytesToHexString(b);
        Socket socket = null;
        try {
            System.out.println("Connecting...");
            socket = new Socket(host, port);
            System.out.println("Connection success");

            byte[] bytes = hexStringToByteArrayWithCRC16(slaveID + "10" + writeAddress + "00 02" + "04" + writeValue); // 按照Modbus RTU拼接报文
            System.out.println("Send: " + bytesToHexString(bytes));
            OutputStream os = socket.getOutputStream();
            BufferedOutputStream bos = new BufferedOutputStream(os);
            bos.write(bytes);
            bos.flush();

            //读取服务器返回的消息
            byte[] receivedBytes = new byte[bytes.length];
            socket.getInputStream().read(receivedBytes);
            System.out.println("Receive: " + bytesToHexString(receivedBytes));
            if (receivedBytes[0] == bytes[0] && receivedBytes[1] == bytes[1]) {
                System.out.println("Write successfully");
            } else {
                System.out.println("ERR");
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (socket != null) {
                try {
                    socket.close();
                } catch (Exception ignored) {

                }
            }
        }
    }

    /**
     * 16进制表示的字符串转换为字节数组，并附加两位CRC16校验
     *
     * @param hexString 16进制表示的字符串
     * @return byte[] 字节数组
     */
    public static byte[] hexStringToByteArrayWithCRC16(String hexString) {
        byte[] bytes = hexStringToByteArray(hexString);
        byte[] bytesWithCRC16 = new byte[bytes.length + 2];
        byte[] crc16 = hexStringToByteArray(getCRC(bytes));
        if (crc16.length == 1) {
            byte crc16_0 = crc16[0];
            crc16 = new byte[2];
            crc16[0] = crc16_0;
            crc16[1] = 0;
        }
        System.arraycopy(bytes, 0, bytesWithCRC16, 0, bytes.length);
        bytesWithCRC16[bytes.length] = crc16[0];
        try {
            bytesWithCRC16[bytes.length + 1] = crc16[1];

        } catch (Exception e) {
            System.out.println(getCRC(bytes));
            e.printStackTrace();
        }
        return bytesWithCRC16;
    }

    /**
     * 16进制表示的字符串转换为字节数组
     *
     * @param hexString 16进制表示的字符串
     * @return byte[] 字节数组
     */
    public static byte[] hexStringToByteArray(String hexString) {
        hexString = hexString.replaceAll(" ", "");
        int len = hexString.length();
        if (len % 2 == 1) {
            hexString = "0" + hexString;
            len++;
        }
        byte[] bytes = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            // 两位一组，表示一个字节,把这样表示的16进制字符串，还原成一个字节
            bytes[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4) + Character
                    .digit(hexString.charAt(i + 1), 16));
        }
        return bytes;
    }

    /**
     * byte[]数组转换为16进制的字符串
     *
     * @param bytes 要转换的字节数组
     * @return 转换后的结果
     */
    public static String bytesToHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte aByte : bytes) {
            String hex = Integer.toHexString(0xFF & aByte);
            if (hex.length() == 1) {
                sb.append('0');
            }
            sb.append(hex);
            sb.append(" ");
        }
        return sb.toString();
    }

    /**
     * 计算CRC16校验码
     *
     * @param bytes 原始16进制数组
     * @return 返回CRC16，类型是String
     */
    public static String getCRC(byte[] bytes) {
        int CRC = 0x0000ffff;
        int POLYNOMIAL = 0x0000a001;

        int i, j;
        for (i = 0; i < bytes.length; i++) {
            CRC ^= ((int) bytes[i] & 0x000000ff);
            for (j = 0; j < 8; j++) {
                if ((CRC & 0x00000001) != 0) {
                    CRC >>= 1;
                    CRC ^= POLYNOMIAL;
                } else {
                    CRC >>= 1;
                }
            }
        }
        CRC = ((CRC & 0x0000FF00) >> 8) | ((CRC & 0x000000FF) << 8);
        return Integer.toHexString(CRC);
    }

    /**
     * 4字节byte数组转float
     *
     * @param b 4字节byte数组
     * @return float数
     */
    public static float bytesToFloat(byte... b) {
        int accum = 0;
        accum = accum | (b[3] & 0xff);
        accum = accum | (b[2] & 0xff) << 8;
        accum = accum | (b[1] & 0xff) << 16;
        accum = accum | (b[0] & 0xff) << 24;
        return Float.intBitsToFloat(accum);
    }

    /**
     * 两位bytes转无符号short，
     * 【特殊说明】java的有short都是有符号的，所以结果需要用int来存，否则像65535就会出错
     *
     * @return 一个int值
     */
    public static int bytesToUnsignedShort(byte high, byte low) {
        return ((high & 0xff) << 8) | (low & 0xff);
    }

    /**
     * 根据点表处理数据，并赋值到实体类中
     *
     * @param rawBytes 原始二进制数据
     * @param offset   偏移量
     * @param entity   数据实体
     * @param p        点表
     */
    public static void setValueToEntityByPointData(byte[] rawBytes, int offset, Object entity, PointInfo p) {
        byte[] b = new byte[p.getLen()];
        System.arraycopy(rawBytes, offset, b, 0, p.getLen());
        switch (p.getType()) {
            case "u8": // 处理8位无符号数
            case "u16":
                // 处理16位无符号数
                Integer us = bytesToUnsignedShort(b[0], b[1]);
                setFieldValueByFieldName(p.getEntityFieldName(), entity, us);
                break;
            case "skip":
                break;
            case "float":
                Float bigDecimal = bytesToFloat(b);
//                log.info("{}", bigDecimal);
                setFieldValueByFieldName(p.getEntityFieldName(), entity, bigDecimal);
                break;
        }
    }

    /**
     * 根据属性名设置属性值
     * https://blog.csdn.net/qiesheng/article/details/79702163
     *
     * @param fieldName 属性名
     * @param object    被赋值的对象实体
     * @param value     此属性的对象
     */
    private static void setFieldValueByFieldName(String fieldName, Object object, Object value) {
        try {
            // 获取obj类的字节文件对象
            Class<?> c = object.getClass();
            // 获取该类的成员变量
            Field f = c.getDeclaredField(fieldName);
            // 取消语言访问检查
            f.setAccessible(true);
            // 给变量赋值
            f.set(object, value);
        } catch (Exception e) {
//            log.error(e.getMessage(), e);
        }
    }

    /**
     * 把 0-65535 之间的数转为2字节的16进制字符串，补上前面的0
     * 例如 1 -> 0001, 255 -> 00ff, 4095 -> 0fff
     * 便于直接拼装到报文里面传输
     *
     * @param a 要转换的数
     * @return 2字节的16进制字符串
     */
    public static String u8To2BytesHexString(int a) {
        String hs = Integer.toHexString(a);
        System.out.println(hs);
        if (a <= 15) {
            hs = "0" + hs;
        }
        if (a <= 255) {
            hs = "0" + hs;
        }
        if (a <= 4095) {
            hs = "0" + hs;
        }
        return hs;
    }

    /**
     * 把 0-255 之间的数转为2字节的16进制字符串，补上前面的0
     * 例如 1 -> 01, 16 -> 10, 255 -> ff
     * 便于直接拼装到报文里面传输
     *
     * @param a 要转换的数
     * @return 1字节的16进制字符串
     */
    public static String u8To1BytesHexString(int a) {
        String hs = Integer.toHexString(a);
        System.out.println(hs);
        if (a <= 15) {
            hs = "0" + hs;
        }
        return hs;
    }
}

class Data {
    float BAT_V;
    float BAT_I;
    int hour;
    int minute;
    float BAT_C;
    float BAT_R;
    float NTC_T;

    int WORK_mode;
    int PAST_state;
    int BAT_I_PID;
    int PID_softstart;
    int NTC_state;
    int current_work_station;
    int FAN_control_state;

    int START_bit;
    int STOP_bit;
    int CYCLES_number;
    float ADD_V;
    float BAT_chargestop_V;
    float BAT_charge_I;
    float BAT_chargestop_I;
    float BAT_dischargestop_V;
    float BAT_discharge_I;
    float BAT_balance_V;
    int Comm0Add;
    int memory_data_bit;
    int RES_CYCLES_number;

    float BAT_V_K;
    float BAT_V_B;
    float BAT_charge_I_K;
    float BAT_charge_I_B;
    float BAT_discharge_I_K;
    float BAT_discharge_I_B;

    public Data() {
    }

    public Data(float BAT_V, float BAT_I, int hour, int minute, float BAT_C, float BAT_R, float NTC_T, int WORK_mode, int PAST_state, int BAT_I_PID, int PID_softstart, int NTC_state, int current_work_station, int FAN_control_state, int START_bit, int STOP_bit, int CYCLES_number, float ADD_V, float BAT_chargestop_V, float BAT_charge_I, float BAT_chargestop_I, float BAT_dischargestop_V, float BAT_discharge_I, float BAT_balance_I, int comm0Add, int memory_data_bit, int RES_CYCLES_number, float BAT_V_K, float BAT_V_B, float BAT_charge_I_K, float BAT_charge_I_B, float BAT_discharge_I_K, float BAT_discharge_I_B) {
        this.BAT_V = BAT_V;
        this.BAT_I = BAT_I;
        this.hour = hour;
        this.minute = minute;
        this.BAT_C = BAT_C;
        this.BAT_R = BAT_R;
        this.NTC_T = NTC_T;
        this.WORK_mode = WORK_mode;
        this.PAST_state = PAST_state;
        this.BAT_I_PID = BAT_I_PID;
        this.PID_softstart = PID_softstart;
        this.NTC_state = NTC_state;
        this.current_work_station = current_work_station;
        this.FAN_control_state = FAN_control_state;
        this.START_bit = START_bit;
        this.STOP_bit = STOP_bit;
        this.CYCLES_number = CYCLES_number;
        this.ADD_V = ADD_V;
        this.BAT_chargestop_V = BAT_chargestop_V;
        this.BAT_charge_I = BAT_charge_I;
        this.BAT_chargestop_I = BAT_chargestop_I;
        this.BAT_dischargestop_V = BAT_dischargestop_V;
        this.BAT_discharge_I = BAT_discharge_I;
        this.BAT_balance_V = BAT_balance_I;
        Comm0Add = comm0Add;
        this.memory_data_bit = memory_data_bit;
        this.RES_CYCLES_number = RES_CYCLES_number;
        this.BAT_V_K = BAT_V_K;
        this.BAT_V_B = BAT_V_B;
        this.BAT_charge_I_K = BAT_charge_I_K;
        this.BAT_charge_I_B = BAT_charge_I_B;
        this.BAT_discharge_I_K = BAT_discharge_I_K;
        this.BAT_discharge_I_B = BAT_discharge_I_B;
    }

    public float getBAT_V() {
        return BAT_V;
    }

    public void setBAT_V(float BAT_V) {
        this.BAT_V = BAT_V;
    }

    public float getBAT_I() {
        return BAT_I;
    }

    public void setBAT_I(float BAT_I) {
        this.BAT_I = BAT_I;
    }

    public int getHour() {
        return hour;
    }

    public void setHour(int hour) {
        this.hour = hour;
    }

    public int getMinute() {
        return minute;
    }

    public void setMinute(int minute) {
        this.minute = minute;
    }

    public float getBAT_C() {
        return BAT_C;
    }

    public void setBAT_C(float BAT_C) {
        this.BAT_C = BAT_C;
    }

    public float getBAT_R() {
        return BAT_R;
    }

    public void setBAT_R(float BAT_R) {
        this.BAT_R = BAT_R;
    }

    public float getNTC_T() {
        return NTC_T;
    }

    public void setNTC_T(float NTC_T) {
        this.NTC_T = NTC_T;
    }

    public int getWORK_mode() {
        return WORK_mode;
    }

    public void setWORK_mode(int WORK_mode) {
        this.WORK_mode = WORK_mode;
    }

    public int getPAST_state() {
        return PAST_state;
    }

    public void setPAST_state(int PAST_state) {
        this.PAST_state = PAST_state;
    }

    public int getBAT_I_PID() {
        return BAT_I_PID;
    }

    public void setBAT_I_PID(int BAT_I_PID) {
        this.BAT_I_PID = BAT_I_PID;
    }

    public int getPID_softstart() {
        return PID_softstart;
    }

    public void setPID_softstart(int PID_softstart) {
        this.PID_softstart = PID_softstart;
    }

    public int getNTC_state() {
        return NTC_state;
    }

    public void setNTC_state(int NTC_state) {
        this.NTC_state = NTC_state;
    }

    public int getCurrent_work_station() {
        return current_work_station;
    }

    public void setCurrent_work_station(int current_work_station) {
        this.current_work_station = current_work_station;
    }

    public int getFAN_control_state() {
        return FAN_control_state;
    }

    public void setFAN_control_state(int FAN_control_state) {
        this.FAN_control_state = FAN_control_state;
    }

    public int getSTART_bit() {
        return START_bit;
    }

    public void setSTART_bit(int START_bit) {
        this.START_bit = START_bit;
    }

    public int getSTOP_bit() {
        return STOP_bit;
    }

    public void setSTOP_bit(int STOP_bit) {
        this.STOP_bit = STOP_bit;
    }

    public int getCYCLES_number() {
        return CYCLES_number;
    }

    public void setCYCLES_number(int CYCLES_number) {
        this.CYCLES_number = CYCLES_number;
    }

    public float getADD_V() {
        return ADD_V;
    }

    public void setADD_V(float ADD_V) {
        this.ADD_V = ADD_V;
    }

    public float getBAT_chargestop_V() {
        return BAT_chargestop_V;
    }

    public void setBAT_chargestop_V(float BAT_chargestop_V) {
        this.BAT_chargestop_V = BAT_chargestop_V;
    }

    public float getBAT_charge_I() {
        return BAT_charge_I;
    }

    public void setBAT_charge_I(float BAT_charge_I) {
        this.BAT_charge_I = BAT_charge_I;
    }

    public float getBAT_chargestop_I() {
        return BAT_chargestop_I;
    }

    public void setBAT_chargestop_I(float BAT_chargestop_I) {
        this.BAT_chargestop_I = BAT_chargestop_I;
    }

    public float getBAT_dischargestop_V() {
        return BAT_dischargestop_V;
    }

    public void setBAT_dischargestop_V(float BAT_dischargestop_V) {
        this.BAT_dischargestop_V = BAT_dischargestop_V;
    }

    public float getBAT_discharge_I() {
        return BAT_discharge_I;
    }

    public void setBAT_discharge_I(float BAT_discharge_I) {
        this.BAT_discharge_I = BAT_discharge_I;
    }

    public float getBAT_balance_V() {
        return BAT_balance_V;
    }

    public void setBAT_balance_V(float BAT_balance_V) {
        this.BAT_balance_V = BAT_balance_V;
    }

    public int getComm0Add() {
        return Comm0Add;
    }

    public void setComm0Add(int comm0Add) {
        Comm0Add = comm0Add;
    }

    public int getMemory_data_bit() {
        return memory_data_bit;
    }

    public void setMemory_data_bit(int memory_data_bit) {
        this.memory_data_bit = memory_data_bit;
    }

    public int getRES_CYCLES_number() {
        return RES_CYCLES_number;
    }

    public void setRES_CYCLES_number(int RES_CYCLES_number) {
        this.RES_CYCLES_number = RES_CYCLES_number;
    }

    public float getBAT_V_K() {
        return BAT_V_K;
    }

    public void setBAT_V_K(float BAT_V_K) {
        this.BAT_V_K = BAT_V_K;
    }

    public float getBAT_V_B() {
        return BAT_V_B;
    }

    public void setBAT_V_B(float BAT_V_B) {
        this.BAT_V_B = BAT_V_B;
    }

    public float getBAT_charge_I_K() {
        return BAT_charge_I_K;
    }

    public void setBAT_charge_I_K(float BAT_charge_I_K) {
        this.BAT_charge_I_K = BAT_charge_I_K;
    }

    public float getBAT_charge_I_B() {
        return BAT_charge_I_B;
    }

    public void setBAT_charge_I_B(float BAT_charge_I_B) {
        this.BAT_charge_I_B = BAT_charge_I_B;
    }

    public float getBAT_discharge_I_K() {
        return BAT_discharge_I_K;
    }

    public void setBAT_discharge_I_K(float BAT_discharge_I_K) {
        this.BAT_discharge_I_K = BAT_discharge_I_K;
    }

    public float getBAT_discharge_I_B() {
        return BAT_discharge_I_B;
    }

    public void setBAT_discharge_I_B(float BAT_discharge_I_B) {
        this.BAT_discharge_I_B = BAT_discharge_I_B;
    }

    @Override
    public String toString() {
        return "Data{" +
                "BAT_V=" + BAT_V +
                ", BAT_I=" + BAT_I +
                ", hour=" + hour +
                ", minute=" + minute +
                ", BAT_C=" + BAT_C +
                ", BAT_R=" + BAT_R +
                ", NTC_T=" + NTC_T +
                ", WORK_mode=" + WORK_mode +
                ", PAST_state=" + PAST_state +
                ", BAT_I_PID=" + BAT_I_PID +
                ", PID_softstart=" + PID_softstart +
                ", NTC_state=" + NTC_state +
                ", current_work_station=" + current_work_station +
                ", FAN_control_state=" + FAN_control_state +
                ", START_bit=" + START_bit +
                ", STOP_bit=" + STOP_bit +
                ", CYCLES_number=" + CYCLES_number +
                ", ADD_V=" + ADD_V +
                ", BAT_chargestop_V=" + BAT_chargestop_V +
                ", BAT_charge_I=" + BAT_charge_I +
                ", BAT_chargestop_I=" + BAT_chargestop_I +
                ", BAT_dischargestop_V=" + BAT_dischargestop_V +
                ", BAT_discharge_I=" + BAT_discharge_I +
                ", BAT_balance_I=" + BAT_balance_V +
                ", Comm0Add=" + Comm0Add +
                ", memory_data_bit=" + memory_data_bit +
                ", RES_CYCLES_number=" + RES_CYCLES_number +
                ", BAT_V_K=" + BAT_V_K +
                ", BAT_V_B=" + BAT_V_B +
                ", BAT_charge_I_K=" + BAT_charge_I_K +
                ", BAT_charge_I_B=" + BAT_charge_I_B +
                ", BAT_discharge_I_K=" + BAT_discharge_I_K +
                ", BAT_discharge_I_B=" + BAT_discharge_I_B +
                '}';
    }
}

class PointTable {
    public static List<PointInfo> pointTable = null;

    static {
        pointTable = new ArrayList<>();
        pointTable.add(new PointInfo("float", 4, "BAT_V", "电压", "V"));
        pointTable.add(new PointInfo("float", 4, "BAT_I", "电流", "A"));
        pointTable.add(new PointInfo("u16", 2, "hour", "时间小时", "h"));
        pointTable.add(new PointInfo("u16", 2, "minute", "时间分钟", "min"));
        pointTable.add(new PointInfo("float", 4, "BAT_C", "容量", "AH"));
        pointTable.add(new PointInfo("float", 4, "BAT_R", "内阻", "mΩ"));
        pointTable.add(new PointInfo("float", 4, "NTC_T", "温度", "℃"));
        pointTable.add(new PointInfo("u8", 2, "WORK_mode", "工作模式", ""));
        pointTable.add(new PointInfo("u8", 2, "PAST_state", "过去状态", ""));
        pointTable.add(new PointInfo("u8", 2, "BAT_I_PID", "PID电流", ""));
        pointTable.add(new PointInfo("u8", 2, "PID_softstart", "PID软起动标志位", ""));
        pointTable.add(new PointInfo("u8", 2, "NTC_state", "温感状态", ""));
        pointTable.add(new PointInfo("u8", 2, "current_work_station", "当前工作状态", ""));
        pointTable.add(new PointInfo("u8", 2, "FAN_control_state", "风扇控制状态", ""));
//        pointTable.add(new PointInfo("skip", 12, "skip", "跳过",""));
        pointTable.add(new PointInfo("u8", 2, "START_bit", "启动标志位", ""));
        pointTable.add(new PointInfo("u8", 2, "STOP_bit", "停止标志位", ""));
        pointTable.add(new PointInfo("u8", 2, "CYCLES_number", "循环次数", "次"));
        pointTable.add(new PointInfo("float", 4, "ADD_V", "补充电压", "V"));
        pointTable.add(new PointInfo("float", 4, "BAT_chargestop_V", "充电电压", "V"));
        pointTable.add(new PointInfo("float", 4, "BAT_charge_I", "充电电流", "A"));
        pointTable.add(new PointInfo("float", 4, "BAT_chargestop_I", "充电截至电流", "A"));
        pointTable.add(new PointInfo("float", 4, "BAT_dischargestop_V", "放电截止电压", "V"));
        pointTable.add(new PointInfo("float", 4, "BAT_discharge_I", "放电电流", "A"));
        pointTable.add(new PointInfo("float", 4, "BAT_balance_V", "均衡电压", "V"));
        pointTable.add(new PointInfo("u8", 4, "Comm0Add", "ID", ""));
        pointTable.add(new PointInfo("u8", 4, "memory_data_bit", "存储历史数据标志", ""));
        pointTable.add(new PointInfo("u8", 4, "RES_CYCLES_number", "剩余循环次数", ""));
        pointTable.add(new PointInfo("float", 4, "BAT_V_K", "电压K值", ""));
        pointTable.add(new PointInfo("float", 4, "BAT_V_B", "电压B值", ""));
        pointTable.add(new PointInfo("float", 4, "BAT_charge_I_K", "充电电流K值", ""));
        pointTable.add(new PointInfo("float", 4, "BAT_charge_I_B", "充电电流B值", ""));
        pointTable.add(new PointInfo("float", 4, "BAT_discharge_I_K", "放电电流K值", ""));
        pointTable.add(new PointInfo("float", 4, "BAT_discharge_I_B", "放电电流B值", ""));
    }
}


class PointInfo {
    String type; // 类型： unsigned short, short, float, string
    int len; // 所占的字节长度
    String entityFieldName; // 对应的java实体类的属性名，务必相同，之后会根据这个属性进行java反射操作
    String nameCN; // 中文含义
    String unit; // 单位

    public PointInfo(String type, int len, String entityFieldName, String nameCN, String unit) {
        this.type = type;
        this.len = len;
        this.entityFieldName = entityFieldName;
        this.nameCN = nameCN;
        this.unit = unit;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public int getLen() {
        return len;
    }

    public void setLen(int len) {
        this.len = len;
    }

    public String getEntityFieldName() {
        return entityFieldName;
    }

    public void setEntityFieldName(String entityFieldName) {
        this.entityFieldName = entityFieldName;
    }

    public String getNameCN() {
        return nameCN;
    }

    public void setNameCN(String nameCN) {
        this.nameCN = nameCN;
    }

    public String getUnit() {
        return unit;
    }

    public void setUnit(String unit) {
        this.unit = unit;
    }
}
